<?php

namespace linghuo\v2;

use think\exception\HttpException;

class Linghuo
{
    protected $puk = '';//服务商公钥
    protected $prik = '';//自己私钥
    protected $host = 'https://fec.51wanquan.com';//接口域名
    public $merchantNo = '';//商户号
    protected $providerNo = '';//服务商号
    public $productCode = '';//产品编号
    public $taskCode = '';//项目编号 任务编号
    protected $usePayPass = '';//支付通道
    protected $accountType = '';//账户类型【1.商户开户2.服务商开户】

    public function __construct() {
        $config = config('thirdpay.linghuo');
        $this->puk = $config['puk'];
        $this->prik = $config['prik'];
        $this->host = $config['host'];
        $this->merchantNo = $config['merchantNo'];//商户号
        $this->providerNo = $config['providerNo'];//服务商号
        $this->productCode = $config['productCode'];//产品编号
        $this->taskCode = $config['taskCode'];//项目编号 任务编号
        $this->usePayPass = $config['usePayPass'];
        $this->accountType = $config['accountType'];

    }
    /**
     * [register 注册]
     * @since  2022-09-24
     * @param  [type]     $name           [description]
     * @param  [type]     $idCard         [description]
     * @param  [type]     $phone          [description]
     * @param  [type]     $imgIdCardFront [description]
     * @param  [type]     $imgIdCardBack  [description]
     * @return [type]                     [description]
     */
    const BUSTYPE_1 = '1';//委托代征
    public function register($name,$idCard,$phone,$imgIdCardFront,$imgIdCardBack,$imgFace='')
    {
        $api = $this->host.'/api/fec/v2/acct/register';
        $json = [
            'providerNo'=>$this->providerNo  ,// 服务商号
            'name'=>$name  ,// 姓名
            'idCard'=>$idCard  ,// 身份证号
            'phone'=>$phone,//  手机号
            'imgIdCardFront'=>$this->fileBase64($imgIdCardFront), //身份证正面 base64格式 Base64编码后的大小不超2M
            'imgIdCardBack'=>$this->fileBase64($imgIdCardBack),  //身份证反面 base64格式 Base64编码后的大小不超2M
            'busType'=>self::BUSTYPE_1, //业务类型 1委托代征2.个体户注册(分包)3.自然人代开4.临时税务登记
            // imgFace 否   string  面部高清照，格式要求：Base64值；Base64编码后的大小不超2M
        ];
        if (!empty($imgFace)) {
            $json['imgFace'] = $this->fileBase64($imgFace);
        }
        // echo '请求URL:'.$api."\n";
        // echo '请求参数:'.json_encode($param)."\n";
        // echo '响应内容:'.$res[0]."\n";exit;
        // var_dump($api,json_encode($json));exit;
        
        // 响应
        // statusCode  否   String  业务返回码
        // msg 否   String  返回信息
        // memberId    否   String  商户会员ID
        // systemId    否   String  系统会员ID

        return $this->exec($api,$json);
    }
    protected function fileBase64($filename,$imgtype = '')
    {
        return $imgtype.base64_encode( @file_get_contents($filename));
        // return 'data:image/webp;base64,'.base64_encode( file_get_contents($filename));
    }
    /**
     * [update 绑定/变更结算卡]
     * @since  2022-10-16
     * @param  [type]     $systemId  [description]
     * @param  [type]     $bankNo    [description]
     * @param  [type]     $cardNo    [description]
     * @param  [type]     $bankPhone [description]
     * @param  string     $imgBank   [description]
     * @return [type]                [description]
     */
    public function update($systemId,$bankNo,$cardNo,$bankPhone,$imgBank='')
    {
        $api = $this->host.'/api/fec/v2/acct/update';
        $json = [
            'systemId'=>$systemId,    // 是   string(36)  系统会员ID
            'payPass'=>$this->usePayPass, // 是   string(1)   支付通道（1:众邦, 2: 支付宝, 4: 招商银行） 变更类型为4必传
            'bankNo'=> $bankNo, // 是   string(30)  开户行编号 
            'cardNo'=>$cardNo,  // 是   string(30)  结算卡号 变更类型为4必传
            'bankPhone'=>$bankPhone,   // 是   string(11)  银行预留手机号 变更类型为4必传
            // imgBank // 否   string  银行卡照片 变更类型为4必传(暂时不传, 后期上线后会有变动)
        ];
        if (!empty($imgBank)) {
            $json['imgBank'] = $this->fileBase64($imgBank);
        }
        //响应内容 http://guanj.51wanquan.com/webwanquan/web/?app_act=member/wap/order_pay&store_code=S62660100&order_no=2022101601461516397235935550x&mchid=1639723593&order_pay=1&isNeedShare=2
        // statusCode   否   String  业务返回码
        // msg 否   String  返回信息
        return $this->exec($api,$json);
    }
    /**
     * [query 用户信息查询接口]
     * @since  2022-09-24
     * @param  [type]     $type     [查询类型 1:查询基本信息 2:查询开户信息 3:查询会员ID]
     * @param  [type]     $memberId [会员ID （查询类型 1/2必传）]
     * @param  [type]     $payPass  [支付通道 1:众邦 （查询类型 2 必传）, 2: 支付宝, 4: 招商银行]
     * @param  [type]     $idCard   [身份证号 （查询类型 3 必传）]
     * @return [type]               [description]
     */
    public function query($systemId)
    {
        $api = $this->host.'/api/fec/v2/acct/query';
        $json = [
            'systemId'=>$systemId,//  是(36)   string     系统会员 ID
            'payPass'=>$this->usePayPass,//  否(1)    string  支付通道 1:众邦 （查询类型 2 必传）, 2: 支付宝, 4: 招商银行
        ];
        // 响应内容
        // statusCode  是   String  业务返回码
        // msg 否   String  返回信息
        // name    是   string  姓名 (张三)
        // idCard  是   string  身份证号 （430422XXXX)
        // bankNo  否   string  开户行编号 （ payPass不为空且绑定过银行卡返回）
        // bankName    否   string  开户行 （payPass不为空且绑定过银行卡返回）
        // cardNo  否   string  卡号 （payPass不为空且绑定过银行卡返回）
        // bankPhone   否   string  银行预留手机号 （payPass不为空且绑定过银行卡返回）
        // systemId    是   string(36)  系统会员ID
        // var_dump($json);exit;
        return $this->exec($api,$json);
    }
    const GET_SYSTEM_ID_TYPE_3 = '3'; //根据身份证号
    const GET_SYSTEM_ID_TYPE_4 = '4'; //根据商户会员Id
    /**
     * [getsystemid 用户信息查询接口]
     * @since  2022-09-24
     * @param  [type]     $type     [查询类型 1:查询基本信息 2:查询开户信息 3:查询会员ID]
     * @param  [type]     $memberId [会员ID （查询类型 1/2必传）]
     * @param  [type]     $payPass  [支付通道 1:众邦 （查询类型 2 必传）, 2: 支付宝, 4: 招商银行]
     * @param  [type]     $idCard   [身份证号 （查询类型 3 必传）]
     * @return [type]               [description]
     */
    public function getsystemid($type,$param)
    {
        $api = $this->host.'/api/fec/v2/system/id';
        $json = [
            'type'=>$type,//  是 string(1)   3. 根据身份证号4.根据商户会员Id查询系统会员Id
            // 'payPass'=>$this->usePayPass,//  否(1)    string  支付通道 1:众邦 （查询类型 2 必传）, 2: 支付宝, 4: 招商银行
        ];
        if ($type === self::GET_SYSTEM_ID_TYPE_3) {
            $json['idCard'] = $param;
        }elseif ($type === self::GET_SYSTEM_ID_TYPE_4) {
            $json['memberId'] = $param;
        }
        // 响应内容
        // statusCode  是   String  业务返回码
        // systemId    是   string(36)  系统会员ID
        // var_dump($json);exit;
        return $this->exec($api,$json);
    }

    /**
     * [cards 查询会员绑定的各个通道的结算卡, 可通过通道过滤]
     * @since  2022-09-24
     * @param  [type]     $systemId [系统会员ID ]
     * @return [type]               [description]
     */
    public function cards($systemId)
    {
        $api = $this->host.'/api/fec/v2/bank/cards';
        $json = [
            'systemId'=>$systemId,//  是 string(1)   3. 根据身份证号4.根据商户会员Id查询系统会员Id
            // 'payPass'=>$this->usePayPass,//  否(1)    string  支付通道 1:众邦 （查询类型 2 必传）, 2: 支付宝, 4: 招商银行
        ];
        if ($type === self::GET_SYSTEM_ID_TYPE_3) {
            $json['idCard'] = $param;
        }elseif ($type === self::GET_SYSTEM_ID_TYPE_4) {
            $json['memberId'] = $param;
        }
        // 响应内容
        // memberCardList  否   list    绑卡列表, 详见以下list
        // payPass 否   string  支付通道 （1.众邦银行2.支付宝4.招商银行）
        // izDefault   否   string  是否通道默认提现卡【0否1是】
        // bankNo  否   string  开户行编号
        // bankName    否   string  开户行
        // cardNo  否   string  银行卡号
        // bankPhone   否   string  银行预留手机号
        return $this->exec($api,$json);
    }
    /**
     * [banklist 查询通道支持的银行列表]
     * @since  2022-09-24
     * @param  [type]     $systemId [系统会员ID ]
     * @return [type]               [description]
     */
    public function banklist($systemId)
    {
        $api = $this->host.'/api/fec/v2/bank/list';
        $json = [
            'payPass'=>$this->usePayPass,//  否(1)    string  支付通道 1:众邦 （查询类型 2 必传）, 2: 支付宝, 4: 招商银行
        ];
       
        // 响应内容
        // bankList    否   list    通道支持的银行列表, 详见以下list
        // title   否   string  银行名称
        // text    否   string  银行名称
        // value   否   string  银行编号
        return $this->exec($api,$json);
    }
    const COST_UNDERTAKER_1 = '1';//手续费承担方 1:企业承担
    const COST_UNDERTAKER_2 = '2';//手续费承担方 2:客户承担
    const ACCOUNT_TYPE_1 = '1';//账户类型 1:商户开户
    const ACCOUNT_TYPE_2 = '2';//账户类型 2:服务商开户
    /**
     * [pay 支付]
     * @since  2022-09-25
     * @param  [type]     $memberId       [description]
     * @param  [type]     $changeType     [description]
     * @param  [type]     $phone          [description]
     * @param  string     $imgIdCardFront [description]
     * @param  string     $imgIdCardBack  [description]
     * @param  string     $bankNo         [description]
     * @param  string     $cardNo         [description]
     * @param  string     $bankPhone      [description]
     * @return [type]                     [description]
     */
    public function pay($payList)
    {
        $api = $this->host.'/api/fec/v2/order/pay';
        $json = [
            'providerNo'=> $this->providerNo, // 是   string(50)  服务商号
            'accountType'=> $this->accountType, //     是   string(50)  账户类型【1.商户开户2.服务商开户】 对应账户和服务商关系
            'taskCode'=> $this->taskCode, //     是   string(50)  任务编号
            'productCode'=> $this->productCode, //  是   string(50)  产品编号
            'costUndertaker'=> self::COST_UNDERTAKER_1, //   是   string(1)   手续费承担方 1:企业承担 2:客户承担
            'payPass'=> $this->usePayPass, //  是   string(1)   支付通道（1:众邦 2:支付宝, 4: 招商银行）
            'isCustom'=>'0',//    是   string(1)   是否自定义流水号 【0.不启用1.启用】
            'batchNumber'=> date('Ymd-'). sprintf('%05d', rand(0,99999) ) , //  是   string(14)  批次号 格式：20200101-00001 当天日期+五位序列号（isCustom=0时不为空）
            // batchOrderNo    否   string(14)  不超过20位 自定义流水号 （isCustom=1时不为空）
            'isDefaultCard'=>'1',//    是   string(14)  是否使用默认绑定卡 （0 否1 是 ）【支付宝到户是传1】
        ];
        $json['payList'] = $payList;
        $log = '原始参数：'.to_json($json).PHP_EOL;
        // systemId    是   string(36)  系统会员ID
        // name    是   string(50)  会员名称
        // idCard  是   string(50)  身份证号
        // bankNo  否   string(30)  开户行编号 （isDefaultCard=0必传）
        // bankName    否   string(30)  开户行名称 （isDefaultCard=0必传）
        // cardNo  否   string(30)  结算卡号 （isDefaultCard=0必传）
        // bankPhone   否   string(11)  结算卡号银行预留手机号 （isDefaultCard=0必传）
        // aliPayNo    否   string(50)  支付宝号 发放到支付宝账号必传
        // fee 是   string(20)  金额 单位元，精确到两位小数点 示例： 10.05元
        $payres = $this->exec($api,$json);
        $log .= '响应内容：'.to_json($payres).PHP_EOL;
        // echo $log;exit;
        \think\Log::record($log,'thirdpay');
        return $payres;
    }
    /**
     * [acctlist 商户账户列表查询接口]
     * @since  2022-09-30
     * @return [type]     [description]
     */
    public function acctlist()
    {
        $api = $this->host.'/api/fec/v2/acct/query/list';
        $json = [
            'providerNo'=> $this->providerNo, // 是   string(50)  服务商号
            'payPass'=> $this->usePayPass, //  是   string(1)   支付通道（1:众邦 2:支付宝, 4: 招商银行）格式：20200101-00001 当天日期+五位序列号
        ];

        // memberId    是   string(36)  会员ID
        // name    是   string(50)  会员名称
        // idCard  是   string(50)  身份证号
        // fee 是   string(20)  金额 单位元，精确到两位小数点 示例： 10.05元
        // aliPayNo    否   string(50)  支付宝号 支付通道2 必传
        return $this->exec($api,$json);
    }
    /**
     * [info 商户是否有钱]
     * @since  2022-09-30
     * @return [type]     [description]
     */
    public function info()
    {
        $api = $this->host.'/api/fec/v2/acct/info';
        $json = [
            'accountType'=> $this->accountType,// 账户类型1商户开户2服务商开户
            'providerNo'=> $this->providerNo, // 是   string(50)  服务商号
            'payPass'=> $this->usePayPass, //  是   string(1)   支付通道（1:众邦 2:支付宝, 4: 招商银行）格式：20200101-00001 当天日期+五位序列号
        ];
        // memberId    是   string(36)  会员ID
        // name    是   string(50)  会员名称
        // idCard  是   string(50)  身份证号
        // fee 是   string(20)  金额 单位元，精确到两位小数点 示例： 10.05元
        // aliPayNo    否   string(50)  支付宝号 支付通道2 必传
        return $this->exec($api,$json);
    }
    
    // 支付状态
    const detail_payStatus_0 = '0';//待支付
    const detail_payStatus_1 = '1';//1:支付中
    const detail_payStatus_2 = '2';//2:支付成功
    const detail_payStatus_3 = '3';//3:支付失败
    const detail_payStatus_4 = '4';//4:已撤回
    /**
     * [detail 子订单详情查询接口]
     * @since  2022-09-24
     * @param  [type]     $orderNo     [交易流水号(50)]
     * @return [type]               [description]
     */
    public function detail($orderNo)
    {
        $api = $this->host.'/api/fec/v2/order/query/detail';
        $json = [
            'isCustom'=>'0',//    是   string(50)  是否自定义流水号 【0.否1.是】
            'orderNo'=>$orderNo,// 是   交易流水号(50)
            // batchOrderNo    否   string(50)  自定义批次号 isCustom=1时必传
        ];
        // 返回数据
        // statusCode  是   string  业务返回码
        // msg 否   string  返回信息
        // orderNo 否   string  交易流水号
        // name    否   string  会员名称
        // idCard  否   string  身份证号
        // fee 否   string  金额
        // payStatus   否   string  支付状态[0:待支付 1:支付中 2:支付成功 3:支付失败4:已撤回]
        // payTime 否   string  支付时间 格式：yyyyMMddHHmmss
        // remark  否   string  备注
        return $this->exec($api,$json);
    }

    protected function exec($api,$json)
    {
        $res = ['code'=>0,'msg'=>''];
        try {
            $param = $this->sign($json);
        } catch (HttpException $e) {
            $res['msg'] = $e->getMessage();
            return $res;
        }
        $log = '原始参数：'.to_json($json).PHP_EOL;

        $httpRes = $this->curl_post($api,$param);
        $log .= '响应内容：'. ( !empty($httpRes[0]) ? $httpRes[0]: ('http_code:'.$httpRes[1])).PHP_EOL;
        if (empty($httpRes[0])) {
            $res['msg'] = '请求灵活用工失败';
            \think\Log::record($log,'thirdpay');
            return $res;
        }
        $arr = json_decode($httpRes[0],true);
        $res['data'] = $arr;
        if ($arr['code'] != 200) {
            $res['msg'] = $arr['message'];
            \think\Log::record($log,'thirdpay');
            return $res;
        }
        if ($arr['result']['statusCode'] != '1000') {
            $res['msg'] = isset($arr['result']['msg'])?$arr['result']['msg']:('失败码：'.$arr['result']['statusCode']);
            \think\Log::record($log,'thirdpay');
            return $res;
        }
        return ['code'=>1,'msg'=>'','data'=>$arr];
    }
    protected function curl_post($url,$uriParam)
    {
        $curl = curl_init(); // 启动一个CURL会话
        curl_setopt($curl, CURLOPT_URL, $url); // 要访问的地址
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0); // 对认证证书来源的检查
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2); // 从证书中检查SSL加密算法是否存在
        // curl_setopt($curl, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); // 模拟用户使用的浏览器 使用命令不执行
        curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1); // 使用自动跳转
        curl_setopt($curl, CURLOPT_AUTOREFERER, 1); // 自动设置Referer
        curl_setopt($curl, CURLOPT_POST, 1); // 发送一个常规的Post请求
        curl_setopt($curl, CURLOPT_POSTFIELDS,to_json($uriParam)); // Post提交的数据包
        // curl_setopt($curl, CURLOPT_POSTFIELDS,http_build_query($uriParam)); // Post提交的数据包
        curl_setopt($curl, CURLOPT_TIMEOUT, 30); // 设置超时限制防止死循环
        curl_setopt($curl, CURLOPT_HEADER, 0); // 显示返回的Header区域内容
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); // 获取的信息以文件流的形式返回
        curl_setopt($curl, CURLOPT_HTTPHEADER,['Content-type:application/json']); // 设置头部
        // curl_setopt($curl, CURLOPT_HTTPHEADER,['Content-type:application/x-www-form-urlencoded']); // 设置头部
        $res = curl_exec($curl); // 执行操作
        $errno = curl_errno($curl);
        curl_close($curl); // 关闭CURL会话
        return [$res,$errno];
    }
    protected function sign($json)
    {
        // 加密规则
        // AES使用的算法和填充模式为AES/ECB/PKCS5Padding, RSA使用SHA1WithRSA, AES秘钥长度24字节.
        // requestData：首先将具体业务的接口请求参数按照对应的要求组装成完整的json数据格式字符串，将json字符串进行AES加密后得到A密文。再将A密文进行base64编码，把编码后的数据赋值到requestData.
        // 即: requestData = base64(aes(to_json_string(origin_data), aes_key)))
        $json = is_string($json)?$json:to_json($json);

        $aes_key = \fast\Random::alnum(24);
        // $cipher='AES-128-ECB';
        $cipher='AES-192-ECB';
        $requestData = openssl_encrypt($json, $cipher, $aes_key);
        // var_dump($requestData);exit;
        // encryptKey： 使用系统公钥(SYSTEM_PUBLIC_KEY, 由我方提供)对AES密钥进行RSA加密，再将密文进行base64编码，编码后的密文赋值给encryptKey。AES密钥可以在每次加密过程中都重新生成不一样的值，使敏感数据在传输过程中更加安全。
        // 即: encryptKey = base64(rsa.encrypt(aes_key, system_public_key))
        $res = "-----BEGIN PUBLIC KEY-----\n" . wordwrap(trim($this->puk), 64, "\n", true) . "\n-----END PUBLIC KEY-----";
        // var_dump($res);exit;
        if (!openssl_pkey_get_public($res)) {
            throw new HttpException(400,'请检查服务商RSA公钥配置');
        }
        $res = openssl_public_encrypt($aes_key, $encryptKey, $res,OPENSSL_PKCS1_PADDING); //OPENSSL_ALGO_SHA256是php5.4.8以上版本才支持
        if (!$res) {
            throw new HttpException(400,'服务商RSA公钥加密失败');
        }
        $encryptKey = base64_encode($encryptKey);


        list($microsecond , $time) = explode(' ', microtime());
        $microsecond =1;
        $microsecond = sprintf('%3d',$microsecond*1000);
        $param = [
            'requestTime'=>date('YmdHis'). ($microsecond === '1000'?999:$microsecond),//发起请求的时间，格式：yyyyMMddHHmmssSSS
            'nonce'=>\fast\Random::alnum(32),//随机字符串
            'merchantNo'=>$this->merchantNo,//商户号
            'requestData'=>$requestData,//商户号
            'encryptKey'=>$encryptKey,//商户号
        ];
        $str = implode('', $param);

        // sign：按照requestTime + nonce + merchantNo + requestData + encryptKey的顺序将此拼接起来然后RSA商户秘钥(对接方私钥, 并提供对接方的公钥给到我方)签名，签名后的密文后赋值给sign参数。这里的+号仅表示字符串连接, 不计入字符串里.

        // 即: sign = rsa.sign(format!(“{}{}{}{}{}”, requestTime, nonce, merchantNo, requestData, encryptKey), private_key)
        $res = "-----BEGIN RSA PRIVATE KEY-----\n" .
            wordwrap($this->prik, 64, "\n", true) .
            "\n-----END RSA PRIVATE KEY-----";
        if (!openssl_pkey_get_private($res)) {
            throw new HttpException(400,'请检查RSA私钥配置');
        }
        // if ("RSA2" == $signType) {

        // $res = openssl_sign($str, $sign, $res, OPENSSL_ALGO_SHA256); //OPENSSL_ALGO_SHA256是php5.4.8以上版本才支持

        // } else {
            $res = openssl_sign($str, $sign, $res);
        // }
        if (!$res) {
            throw new HttpException(400,'私钥签名失败');
        }
        $param['sign'] = base64_encode($sign);
        return $param;
    }
}
